package com.study.sbsummary.utils;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MongoUtil {
    private static final Map<Class, Map<String, Field>> DOC_FIELDS = new HashMap<>();
    public static final Map<String, DbCollectionPair> COLLECTIONS = new HashMap<>();

    static {
        addDbCollectionPair(new DbCollectionPair("default", "news", "displayDocument"));
        addDbCollectionPair(new DbCollectionPair("V", "video", "displayDocument"));
        addDbCollectionPair(new DbCollectionPair("T", "shortnews", "displayDocument"));
        addDbCollectionPair(new DbCollectionPair("O", "news", "displayDocument"));
    }

    public static void addDbCollectionPair(DbCollectionPair dbCollectionPair) {
        COLLECTIONS.put(dbCollectionPair.getPrefix(), dbCollectionPair);
    }

    public static DbCollectionPair dbWithCollections(String docId) {
        String prefix = idPrefix(docId);

        if (COLLECTIONS.containsKey(prefix)) {
            return COLLECTIONS.get(prefix);
        }
        return COLLECTIONS.get("default");
    }

    public static String database(String docId) {
        String prefix = idPrefix(docId);

        if (COLLECTIONS.containsKey(prefix)) {
            return COLLECTIONS.get(prefix).getDb();
        }
        return COLLECTIONS.get("default").getDb();
    }

    public static String collectionName(String docId) {
        String prefix = idPrefix(docId);
        if (COLLECTIONS.containsKey(prefix)) {
            return COLLECTIONS.get(prefix).getCollection();
        }
        return COLLECTIONS.get("default").getCollection();
    }

    private static String idPrefix(String docId) {
        int pos = docId.indexOf('_');
        if (pos < 0) {
            return "default";
        }
        return docId.substring(0, pos);
    }

    public static Update update(Object document, Set<String> docFields, boolean skipNull) {
        Update update = new Update();
        Map<String, Field> fields = getMongoFileds(document.getClass());
        for (String docField : docFields) {
            Field field = fields.get(docField);
            if (field != null) {
                setDocField(update, document, field, docField, skipNull);
            }
        }
        return update;
    }

    public static Update update(Object document) {
        return update(document, true, false);
    }

    public static Update update(Object document, boolean skipId) {
        return update(document, true, skipId);
    }

    public static Update update(Object document, boolean skipNull, boolean skipId) {
        Update update = new Update();
        Map<String, Field> fields = ReflectionUtil.fields(document.getClass(), true, true);
        for (Map.Entry<String, Field> entry : fields.entrySet()) {
            if (skipId && entry.getKey().equals("_id")) {
                continue;
            }
            setDocField(update, document, entry.getValue(), entry.getKey(), skipNull);
        }
        return update;
    }

    private static void setDocField(Update update, Object document, Field field, String docField, boolean skipNull) {
        Object value = ReflectionUtil.getValue(document, field);
        if (skipNull && value == null) {
            return;
        }
        update.set(docField, value);
    }

    private static Map getMongoFileds(Class cls) {
        Map mFields = DOC_FIELDS.get(cls);
        if (mFields == null) {
            synchronized (cls.getCanonicalName()) {
                mFields = DOC_FIELDS.get(cls);
                if (mFields == null) {
                    Map<String, Field> fields = ReflectionUtil.fields(cls, true);
                    mFields = new HashMap<>(fields.size());
                    for (Map.Entry entry : fields.entrySet()) {
                        Field field = (Field) entry.getValue();
                        org.springframework.data.mongodb.core.mapping.Field annotation = field.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
                        if (annotation == null) {
                            mFields.put(entry.getKey(), field);
                        } else {
                            mFields.put(annotation.value(), field);
                        }
                    }
                    DOC_FIELDS.put(cls, mFields);
                }
            }
        }
        return mFields;
    }

    @Data
    @AllArgsConstructor
    public static class DbCollectionPair {
        private String prefix;
        private String db;
        private String collection;
    }
}
